package com.yxx.strategy.uploadStrategyImpl;

import com.yxx.exception.ServiceException;
import com.yxx.strategy.UploadStrategy;
import com.yxx.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

/**
 * 抽象上传模板
 * 每种具体的上传策略（如 LocalUploadStrategyImpl等）实现了 UploadStrategy 接口
 *  exists、upload 和 getFileAccessUrl 是抽象方法，没有提供默认实现，这种设计更符合“抽象类” + “钩子方法” 的组合方式
 *  抽象上传模板（抽象类+钩子方法）：通过这种方式可以明确 AbstractUploadStrategyImpl 类作为一个“模板”，并通过抽象方法暴露了上传过程中的钩子（具体步骤）。这些步骤可以由子类实现，因此提供了灵活的扩展方式和复用性。
 * @author yuxiangxun
 * @date 2025/1/26
 * @apiNote
 */
@Slf4j
@Service
public abstract class AbstractUploadStrategyImpl implements UploadStrategy {
    @Override
    public String uploadFile(MultipartFile file, String path) {
        try {
            // 获取文件md5值
            String md5 = FileUtils.getMd5(file.getInputStream());
            // 获取文件扩展名
            String extName = FileUtils.getExtension(file);
            // 重新生成文件名
            String fileName = md5 + "." + extName;
            // 判断文件是否已存在
            if (!exists(path + fileName)) {
                // 不存在则继续上传
                upload(path, fileName, file.getInputStream());
            }
            // 返回文件访问路径
            return getFileAccessUrl(path + fileName);
        } catch (Exception e) {
            log.info("uploadFile fail, error is {}", e.getMessage());
            throw new ServiceException("文件上传失败");
        }
    }


    /**
     * 判断文件是否存在
     *
     * @param filePath 文件路径
     * @return {@link Boolean}
     */
    public abstract Boolean exists(String filePath);

    /**
     * 上传
     * @param path        路径
     * @param fileName    文件名
     * @param inputStream 输入流
     * @throws IOException io异常
     */
    public abstract void upload(String path, String fileName, InputStream inputStream) throws IOException;

    /**
     * 获取文件访问url
     *
     * @param filePath 文件路径
     * @return {@link String} 文件url
     */
    public abstract String getFileAccessUrl(String filePath);
}
